Skip to content

feat(sdk): provider mode as a docker-browser wrapper - #22

Merged
iWedmak merged 6 commits into
masterfrom
feature/provider-docker
Sep 4, 2026
Merged

feat(sdk): provider mode as a docker-browser wrapper#22
iWedmak merged 6 commits into
masterfrom
feature/provider-docker

Conversation

@iWedmak

@iWedmak iWedmak commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

What & why

ceki provider run no longer reimplements the provider launcher. It pulls and runs the official image ceki/provider (Docker Hub), whose launcher lives in the public repo Ceki-me/docker-browser — the single source of truth. The SDK only orchestrates docker.

Rework of #18 (which duplicated the launcher as _provider.py, had a dangling upload_file and failing CI). Branch is fresh off master.

Commits

  1. 346a020 fix(sdk): daemon session-death cleanup + shared client WS reuse (ev 6621) — salvaged from feat(sdk): headless-browser provider mode #18, still needed: void commands (navigate/click/type/...) returning null were treated as "daemon down" → fell back to one-shot resume and killed the rented session; plus a per-rent WebSocket leak on the relay. One Client per api_key, disconnect on last session end.
  2. dfc4bf2 style(client): wrap overlong cdp_response debug line (E501) — pre-existing line (from P2P WebRTC transport + daemon: seamless CDP over data channel #15) that breaks feature-branch CI; no logic change.
  3. 6839dda feat(sdk): provider mode as a docker-browser wrapper (ev 6745) — the core change.

Provider wrapper

  • ceki provider run [--token --image --build --viewport --timeout --verbose]
  • token/image resolution (arg → env → default), docker presence check, optional local build (--build DIRbuild.sh), image inspect/pull, docker run --rm
  • only public provider envs passed through (CEKI_PROVIDER_VIEWPORT/CEKI_PROVIDER_LOG_LEVEL, TZ, DISPLAY); internal docker-browser envs (CEKI_WS_URL/CEKI_API_URL/update knobs) are not part of the SDK contract
  • --timeout appended as the image's default command (docker run args replace image CMD) → container self-stops after N seconds
  • docker stop / Ctrl-C → clean shutdown (provider is PID 1, handles SIGTERM/SIGINT)

Tests

  • tests/test_provider.py (new): token/env/image resolution, env map (internal envs not forwarded), docker run cmd incl. timeout, inspect+pull+run orchestration, docker-missing/token-required/pull-failure/Ctrl-C paths
  • tests/test_daemon.py (new, in commit 1): shared client per key, rent-failure cleanup, session_ended/-1011 dispatch
  • targeted run: 69 passed, 1 baseline env-flake (test_missing_api_key_exits_2 — fails on clean master too; unrelated to this branch)

CI note

Feature-branch CI has been red since ~run #104 (pre-existing ruff errors in _browser.py/_webrtc.py/3 test files + the env-flake above, all present on master). This branch adds zero new ruff/test failures.

Suggest closing

#18 — superseded by this PR.

ceki-plugin added 3 commits August 26, 2026 18:23
…621)

- _client: accept the relay's session_ended alias and event_id field, and
  route error -1011/-1018 (provider death / grace expiry / admin kill) through
  the same cleanup as session.ended. Call an optional _on_session_ended hook
  so the daemon learns about relay-initiated ends.
- daemon: share ONE Client per api_key — all sessions multiplex over a single
  WebSocket. Disconnect only when the last session for a key ends, fixing the
  per-rent WS leak that orphaned live connections on the relay.
- cli: _daemon_request now returns (ok, result). A null result from a void
  command (navigate/click/type/scroll/switch-tab/configure/stop/...) is a
  SUCCESS, not a daemon-down signal — commands no longer fall through to the
  one-shot resume fallback that disconnects and kills the rented session.
- tests: test_daemon.py (shared client, cleanup, session_ended/-1011 paths)
The line predates this branch (from #15) but fails ruff on CI, which runs
on feature/** pushes — so it would have broken this branch's check.
ceki provider run no longer reimplements the provider launcher. It pulls and
runs the official image ceki/provider (Docker Hub), whose launcher lives in
the public repo Ceki-me/docker-browser — the single source of truth for the
provider. The SDK only orchestrates docker:

- _provider: token/image resolution, docker presence check, optional local
  build, image inspect/pull, docker run. Only the public provider envs
  (CEKI_PROVIDER_VIEWPORT/LOG_LEVEL, TZ, DISPLAY) are passed through;
  internal docker-browser envs are not part of the SDK contract.
- --timeout is passed by appending the image's default command, since docker
  run args replace the image CMD; the container self-stops after N seconds.
- docker stop / Ctrl-C reach the provider (PID 1) and shut it down cleanly.

Docs: README provider section (public env table, docker-browser link).
Tests: test_provider.py (token/env/image resolution, env map incl. internal
envs NOT forwarded, docker run cmd, inspect+pull+run orchestration).
iWedmak and others added 3 commits September 3, 2026 11:50
ceki provider run tries ceki/provider:latest (Docker Hub) first; if the pull
fails the run proceeds with the GHCR build ghcr.io/ceki-me/docker-browser:latest
that docker-browser CI publishes on every GitHub Release. An explicit --image
(or $CEKI_PROVIDER_IMAGE) is used as-is and never swapped.
A cached client can go half-dead (relay stops routing rent/match without a
close frame): TCP stays ESTABLISHED, recv() never raises, pongs keep coming,
so every rent through it times out and the daemon 504s forever. On rent
TimeoutError/ConnectionLost the poisoned client is now dropped from the cache
(and its orphan sessions discarded) and the rent is retried ONCE on a fresh
connection.

Also fix the shared-client exception path: only disconnect the cached client
when it still has no sessions and is still the one cached — the old
'created' flag disconnected a shared client that other requests were using.

rent() timeout now also removes the dead future from _pending_rents so a
long-running client (the daemon) does not accumulate one entry per timed-out
rent.
- raw() now checks for tool.startswith('prompts/') and calls _rpc() directly
- prompts/list and prompts/get are MCP protocol methods, not tools
- Allows: ceki contract raw prompts/list '{}' and ceki contract raw prompts/get '{"name":"..."}'
@iWedmak
iWedmak merged commit 39a8c60 into master Sep 4, 2026
0 of 3 checks passed
@iWedmak
iWedmak deleted the feature/provider-docker branch September 4, 2026 16:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant